Thanks for all the info.
I've replaced all uses of anything with "main" to current. See Code section at end.
1
[quote='809385022, DTS Engineer, /thread/766379?answerId=809385022#809385022']
Basically, yes, that's correct. NSMetadataQuery is an older API and, because of that, it's built around the run loop model because that's how basically ALL of our APIs worked.
[/quote]
Is there any newer API that replaces NSMetadataQuery?
2
[quote='809627022, DTS Engineer, /thread/766379?answerId=809627022#809627022']
The key point here is that there isn't any difference between CFRunLoopRun and RunLoop.run
[/quote]
I think that confused me because there's actually a difference between the following two:
CFRunLoopRun()
RunLoop.run()
but not between the following two:
CFRunLoopRun()
RunLoop.run(mode:before:)
as per:
[quote='809786022, DTS Engineer, /thread/766379?answerId=809786022#809786022']
Because of a subtle (but documented) detail of run's implementation. The RunLoop.run() documentation says:
Puts the receiver into a permanent loop, during which time it processes data from all attached input sources.
Now, compare that to the documentation of RunLoop.run(mode:before:):
Runs the loop once, blocking for input in the specified mode until a given date.
The word "Runs the loop once" are the magic words. RunLoop.run(mode:before:) is the "core" API and, in fact, CFRunLoopStop works fine with it. RunLoop.run doesn't return because it's actual swift implementation would (literally) be
[/quote]
Code
I've changed 4 lines from my original code. Each has a numbered // CHANGE comment at the end of the changed line.
Are there any better solutions than the changes that I've made?
import Foundation
import PromiseKit
guard CommandLine.arguments.count > 1 else {
print("Missing adamID argument")
exit(1)
}
guard let adamID = UInt64(CommandLine.arguments[1]) else {
print("adamID argument must be a UInt64")
exit(2)
}
_ = appInfo(forAdamID: adamID)
.done { appInfo in
if let jsonData = try? JSONSerialization.data(withJSONObject: appInfo),
let jsonString = String(data: jsonData, encoding: .utf8)
{
print(jsonString.replacingOccurrences(of: "\\/", with: "/"))
}
CFRunLoopStop(CFRunLoopGetCurrent()) // CHANGE 1
}
CFRunLoopRun() // CHANGE 2
func appInfo(forAdamID adamID: UInt64) -> Promise<[String: Any]> {
Promise { seal in
let query = NSMetadataQuery()
query.predicate = NSPredicate(format: "kMDItemAppStoreAdamID == %d", adamID)
query.searchScopes = ["/Applications"]
var observer: NSObjectProtocol?
observer = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSMetadataQueryDidFinishGathering,
object: query,
queue: .current // CHANGE 3
) { _ in
query.stop()
defer {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
}
var appInfo: [String: Any] = [:]
for result in query.results {
if let result = result as? NSMetadataItem {
var attributes = ["kMDItemPath"]
attributes.append(contentsOf: result.attributes)
for attribute in attributes {
let value = result.value(forAttribute: attribute)
switch value {
case let date as Date:
appInfo[attribute] = ISO8601DateFormatter().string(from: date)
default:
appInfo[attribute] = value
}
}
}
}
seal.fulfill(appInfo)
}
RunLoop.current.perform { // CHANGE 4
query.start()
}
}
}